#include <chrono>
#include <iostream>
#include <semaphore>
#include <thread>
#include <chrono>
using namespace std;

void get_cmd();

bool flag_sem_timeout = false;
std::counting_semaphore cnt_sem(0);
void ThreadProc(string id)
{
    bool ret;
    while(true){
        if(flag_sem_timeout){
            std::cout << id <<"[线程] 21 try_acquire" << std::endl;
            ret = cnt_sem.try_acquire_for(std::chrono::seconds(2));
            if(ret == false)
                continue;
        }
        else{
            std::cout << id <<"[线程] 21 acquire" << std::endl;
            cnt_sem.acquire();
        }
        std::cout << id <<"[线程] 22 获得锁" << std::endl;
        // 模拟处理耗时
        std::this_thread::sleep_for(std::chrono::seconds(3));
        cnt_sem.release();

        std::cout << id << "[线程] 23 释放锁" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main()
{
    // 创建某个工作线程
    std::thread t1(ThreadProc,"lili");
    std::thread t2(ThreadProc,"makr");
    std::cout << "[主] 11 开始\n"; // 消息
    t1.detach();
    t2.detach();
    get_cmd();

    return 0;
}

void get_cmd()
{
    string cmd;
    bool ret;
    string help=" \
    1 获取信号量 \
    2 释放信号量\n \
    t 切换超时获取/释放 \
    T 命令强制获取锁\n \
    h 帮助 \
    q 退出 \
\n";
    cout << help << endl;

    while(true){
        cin >> cmd;
        if(cmd == "1"){
            // bin_sem.acquire(); 尽量不要使用acquire(), 开始运行时, 按1会形成死锁
            ret = cnt_sem.try_acquire();
            if(ret == false)
                cout << "获取信号量失败" << endl;
        }
        else if(cmd == "2"){
            cnt_sem.release();
        }
        else if(cmd == "t")
        {
            flag_sem_timeout = !flag_sem_timeout;
        }
        else if(cmd == "T")
        {
            cnt_sem.acquire();
        }
        else if(cmd == "h"){
            cout << help << endl;
        }
        else if(cmd == "q"){
            exit(0);
        }
        else{
            cerr << "not support cmd: " << cmd << endl;
            cout << help << endl;
        }
    }
}
/*
结论:
线程同步太费脑了, 一旦多线程, 会出现各种不按逻辑的处理顺序.
建议使用 异步事件/事件驱动, 不要用线程同步方式.

 */